package com.sghd.candy.module.account.facade;

import com.fasterxml.jackson.core.type.TypeReference;
import com.sghd.candy.module.account.manager.Account;
import com.sghd.candy.module.account.service.AccountService;
import com.sghd.candy.module.common.event.LoginEvent;
import com.sghd.candy.module.common.event.LogoutEvent;
import com.sghd.candy.module.config.service.ConfigService;
import com.sghd.candy.module.player.manager.Player;
import com.sghd.candy.module.player.service.PlayerService;
import com.sghd.candy.utils.Identity;
import com.sghd.common.console.ConsoleBean;
import com.sghd.common.console.ConsoleCommand;
import com.sghd.common.event.Event;
import com.sghd.common.event.EventBus;
import com.sghd.common.socket.core.Message;
import com.sghd.common.socket.core.ResultCallback;
import com.sghd.common.socket.session.Session;
import com.sghd.common.socket.session.SessionManager;
import com.sghd.common.socket.utils.IpUtils;
import com.sghd.common.utils.codec.CryptUtils;
import com.sghd.common.utils.http.AsyncCallback;
import com.sghd.common.utils.http.AsyncRestOperations;
import com.sghd.common.utils.id.account.AccountIdGenerator;
import com.sghd.common.utils.json.JsonUtils;
import com.sghd.pojo.candy.account.AccountState;
import com.sghd.pojo.candy.account.to.SmsCodeTo;
import com.sghd.pojo.candy.player.vo.LoginReq;
import com.sghd.pojo.fight.game.Result;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.http.HttpEntity;
import org.springframework.stereotype.Component;

import java.util.HashMap;

import static com.sghd.pojo.StaticConfig.RUN_MODE_TEST;

/**
 * @author frank
 * @date 2018-10-17 10:11
 * @desc
 **/
@ConsoleBean
@Component
public class AccountFacadeImpl implements AccountFacade {
    private static final Logger logger = LoggerFactory.getLogger(AccountFacadeImpl.class);
    private final String CRYPT_KEY = "aaa";
    @Value("${run.mode}")
    private int runMode;
    @Value("${bind.phone.check.url}")
    private String bindPhoneCheckUrl;
    @Value("${token.overtime}")
    private int tokenOverTime;
    @Autowired
    private AsyncRestOperations asyncRestOperations;
    @Autowired
    private AccountService accountService;
    @Autowired
    private PlayerService playerService;
    @Autowired
    private SessionManager sessionManager;

    @Autowired
    private ConfigService configService;

    @Autowired
    private EventBus eventBus;

    @Override
    public Result<Object> login(Session session, LoginReq req) {
        String uid = req.getUid();
        int timestamp = req.getTimestamp();
        int now = (int) (System.currentTimeMillis() / 1000);
        //配置秒超时
        if ((now - timestamp) > tokenOverTime) {
            //登陆超时
            return Result.valueOf(-1, "登陆超时！");
        }
        String token = req.getToken();
        boolean guest = req.isGuest();
        if (!chkLoginKey(uid, timestamp, token, guest, req.getServerId())) {
            //签名验证失败
            return Result.valueOf(-1, "签名验证失败！");
        }
        Account account = null;
        synchronized (Identity.valueOf(uid)) {
            account = accountService.loadByUid(uid);
            if (account == null) {
                account = accountService.create(uid);//创建实体
            }
        }
        if (account.hasState(AccountState.BLOCK)) {
            return Result.valueOf(-1, "该账号已被停封！");
        }
        long playerId = account.getId();
        Player player = playerService.load(playerId);

        if (player == null) {
            player = playerService.createPlayer(playerId);
            if (runMode == RUN_MODE_TEST) {
                configService.testInit(playerId);
            }
        }

        Session prev = sessionManager.getSession(playerId);
        if (prev != null) {
            // 玩家处于在线状态
            prev.write(Message.valueOf(111, "你的账号在别处登陆，您被迫下线！"));
            // 踢原在线玩家下线
            sessionManager.kick(Session.KICK, playerId);
            // 以同步方式发出登出事件，迫使状态恢复到下线状态
            Event<LogoutEvent> event = LogoutEvent.valueOf(player, Session.KICK, prev);
            eventBus.syncPost(event);
        }
        // 设置会话身份
        sessionManager.bind(session, playerId);
        String ip = IpUtils.getIp(session);
        // 登录的逻辑处理
        accountService.login(account, ip, guest, req.getServerId());
        // 发出用户登录事件，通知其他模块完成登录处理
        eventBus.post(LoginEvent.valueOf(player, session));
        return Result.SUCCESS();
    }

    @Override
    public void bindPhone(long playerId, SmsCodeTo smsCodeTo, ResultCallback<Result<String>> callback) {
        Account account = accountService.load(playerId);
        if (account.isGuest()) {
            HashMap<String, String> params = new HashMap<>();
            params.put("uid", account.getUid());
            params.put("phone", smsCodeTo.getPhone());
            params.put("code", smsCodeTo.getvCode());
            HttpEntity<HashMap<String, String>> requestBody = new HttpEntity<>(params);
            asyncRestOperations.postForObject(new AsyncCallback<Result<String>>() {
                @Override
                public void onSuccess(Result<String> result) {
                    if (result.getCode() == 0) {
                        accountService.upgrade(account);
                    }
                    callback.call(result);
                }

                @Override
                public void onError(Exception e) {
                    if (logger.isErrorEnabled()) {
                        logger.error("", e);
                    }
                    callback.call(Result.valueOf(-1));
                }
            }, bindPhoneCheckUrl, requestBody, new TypeReference<Result<String>>() {});
        }else{
            callback.call(Result.valueOf(-1, "已经绑定过手机了！"));
        }
    }

    public static void main(String[] args) {
        System.out.println(AccountIdGenerator.toOperator(4296015873L));
        System.out.println(AccountIdGenerator.toArea(4296015873L));
    }
    @ConsoleCommand(name = "tst6", description = "升级账号测试")
    public void test(long playerId, String phone, String vCode) {
        SmsCodeTo smsCodeTo = new SmsCodeTo();
        smsCodeTo.setPhone(phone);
        smsCodeTo.setvCode(vCode);
        bindPhone(playerId, smsCodeTo, new ResultCallback<Result<String>>() {
            @Override
            public void call(Result<String> result) {
                System.out.println(JsonUtils.object2String(result));
            }

            @Override
            public void callBody(Object o) {
                System.out.println(JsonUtils.object2String(o));
            }
        });
    }

    /** 检查登录是否合法(true:合法,false:不合法) */
    private boolean chkLoginKey(String id, int timestamp, String token, boolean guest, int serverId) {
        try {
            String md5 = CryptUtils.generateToken(CRYPT_KEY, id, timestamp, guest, serverId);
            if (md5.equalsIgnoreCase(token)) {
                return true;
            }
        } catch (Exception e) {
            logger.debug("检查登录密匙时发生异常", e);
        }
        return false;
    }
}
